#ifndef MYMEM_H
#define MYMEM_H
#include <stdint.h>

/**
 * Defines valid allocation modes for the MemoryManager.
 */
enum AllocMode{
  FIRST_FIT,
  BEST_FIT,
  WORST_FIT,
  CUSTOM
};

/**
 * A Node containing information regarding an allocated object.
 * @member size Indicates the size of the memory region allocated to the object.
 * @member data The actual data associated with the object.
 * @member next The location of the next node.
 * @member prev The location of the previous node.
 */
struct NodeType {
  uint32_t size;
  void *data;
  NodeType *next;
  NodeType *prev;
  bool isFree;
};

/**
 * A class implementing region-based memory allocation with semantics similar
 * to malloc and free.
 */
class MemoryManager{
public:
  MemoryManager(const uint32_t memoryNumBytes,
		const uint32_t freeListNumBytes);
  MemoryManager();
  ~MemoryManager();

  void printMemoryStatus(const uint32_t level);
  void printUseList();
  void *mymalloc(const uint32_t requestSize);
  void myfree(void *data);
  void setAllocMode(const AllocMode);

private:
  static const uint32_t DEFAULT_MEM_SIZE = 10000;

  // book-keeping
  NodeType *unusedNodeList;
  NodeType *usedNodeList;
  uint32_t totalSize;
  uint32_t usableSize;
  uint32_t numListNodes;

  // statistics
  uint32_t totalRequestCount;
  uint32_t totalRequestSize;
  uint32_t totalGrantedRequestCount;
  uint32_t totalGrantedRequestSize;
  uint32_t totalFailedRequestCount;
  uint32_t totalFailedRequestSize;
  uint32_t bytesUsed;

  // malloc-granted data block
  enum AllocMode mode;
  void *memory;

  NodeType *getUnusedBlock();
  void releaseUnusedBlock(NodeType *);
  void *allocFirstFit(const uint32_t);
  void *allocBestFit(const uint32_t);
  void *allocWorstFit(const uint32_t);
  void init();

  void *allocNewMemory(NodeType*, const uint32_t);
  void splitMemory(NodeType*, const uint32_t);
  void coalesceMemory(NodeType*);
};
#endif
